﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttlang.type;
using ttlang.type.code;

namespace ttlang.compiler
{
    internal static class compiler_expr
    {
        public static void ParseNode_Number(CompilerContext context, src.AstNode innode, out TTCode outnode, bool inv = false)
        {

            CodeType nodetype = CodeType.CODE_COMMENT;
            string typestr = "";
            switch (innode.type)
            {
                case "INT":
                    nodetype = CodeType.CONST_INT;
                    typestr = "int32";
                    break;
                case "BIN":
                    nodetype = CodeType.CONST_BIN;
                    typestr = "int32";
                    break;
                case "HEX":
                    nodetype = CodeType.CONST_HEX;
                    typestr = "int32";
                    break;
                case "FLOAT":
                    nodetype = CodeType.CONST_FLOAT;
                    typestr = "float";
                    break;
                default:
                    throw new Exception("not supported.");
            }
            ttlang.type.lib.SystemModule.ttlangCore.FindType(typestr, out TTType tttype);



            var v = new TTCode();
            v.type = nodetype;
            v.isExpr = true;//所有的 expr 类型表达式，第一个element 都是返回类型
            v.isConst = true;
            v.elements = new CodeElement[2];
            v.elements[0] = CodeElement.TTType(tttype);
            v.elements[1] = CodeElement.Const_String(inv ? ("-" + innode.text) : innode.text);
            outnode = v;
        }
        public static void ParseNode_String(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            ttlang.type.lib.SystemModule.ttlangCore.FindType("string", out TTType tttype);
            var v = new TTCode();
            v.type = CodeType.CONST_STRING;
            v.isExpr = true;//所有的 expr 类型表达式，第一个element 都是返回类型
            v.elements = new CodeElement[2];
            v.elements[0] = CodeElement.TTType(tttype);
            v.elements[1] = CodeElement.Const_String(innode.text);
            outnode = v;
        }
        public static void ParseNode_Bool(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            ttlang.type.lib.SystemModule.ttlangCore.FindType("bool", out TTType tttype);
            var v = new TTCode();
            v.type = CodeType.CONST_BOOL;
            v.isExpr = true;//所有的 expr 类型表达式，第一个element 都是返回类型
            v.elements = new CodeElement[2];
            v.elements[0] = CodeElement.TTType(tttype);
            v.elements[1] = CodeElement.Const_String(innode.text);
            outnode = v;
        }
        public static void ParseNode_Expr_Member(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            //先试试看能不能处理为idchain
            var inleft = innode.children[0];
            if (inleft.type == "expr" && inleft.children.Count == 1)
                inleft = inleft.children[0];
            ParseNode_IDChain_OnlyExpr(context, inleft, out TTCode outleft);

            //脫殼只要函數名
            var inright = innode.children[2];
            if (inright.type == "expr" && inright.children.Count == 1)
                inright = inright.children[0];
            if (inright.type == "expr_call")
                inright = inright.children[0];
            if (outleft.type == CodeType.EXPR_TEMPVAR)
            {
                TTCode member = new TTCode();
                member.type = CodeType.EXPR_MEMBER;
                member.isExpr = true;
                member.elements = new CodeElement[3];
                var tttype = outleft.GetExprType();
                tttype.FindMember(inright.text, out var _vm);
                if (_vm == null)
                    throw new Exception("should not null.");

                member.elements[0] = CodeElement.TTType(tttype);
                member.elements[1] = CodeElement.TTCode(outleft);
                member.elements[2] = CodeElement.TTMember(_vm);


                outnode = member;

                ////临时变量.b
                ////有两个可能性，成员变量货成员函数，都是成员访问
                return;

            }
            if (outleft.type == CodeType.EXPR_CONTAINER) //Type.xxx // static member or type.type typechain
            {
                var container = outleft.elements[0].tttype;
                //先看看是不是成员访问
                if (container.FindMember(inright.text, out var _vm))
                {
                    TTCode member = new TTCode();
                    member.type = CodeType.EXPR_MEMBER;
                    member.isExpr = true;
                    member.elements = new CodeElement[2];
                    member.elements[0] = CodeElement.TTType(_vm.tttype);
                    member.elements[1] = CodeElement.TTMember(_vm);
                    outnode = member;
                    return;
                }
                if (container.FindSubContainer(inright.text, out var _sc))
                {
                    TTCode newcontainer = new TTCode();
                    newcontainer.type = CodeType.EXPR_CONTAINER;
                    newcontainer.isExpr = true;
                    newcontainer.elements = new CodeElement[1];
                    newcontainer.elements[0] = CodeElement.TTType(_sc);
                    outnode = newcontainer;
                    return;
                }
            }
            throw new NotSupportedException();

        }
        public static bool isConst(src.AstNode innode)
        {
            if (innode.type == "INT" ||
                innode.type == "FLOAT"
                ||
                innode.type == "HEX"
                ||
                innode.type == "BIN")

                return true;
            return false;
        }
        public static void ParseNode_Expr(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            switch (innode.type)
            {
                case "number":
                case "INT":
                case "FLOAT":
                    {
                        ParseNode_Number(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    return;
                case "STRING":
                    {
                        ParseNode_String(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    return;
                case "BOOL":
                    {
                        ParseNode_Bool(context, innode, out TTCode _node);
                        outnode = _node;

                    }
                    return;
                case "ID":
                case "INNERTYPE":
                    {
                        ParseNode_ID_OnlyExpr(context, innode, out outnode);
                    }
                    return;
                case "idchain":
                    {
                        ParseNode_IDChain_OnlyExpr(context, innode, out outnode);
                    }
                    return;
                case "expr_add":
                case "expr_mul":
                case "expr_shift":
                case "expr_relation":
                case "expr_equal":
                case "expr_and":
                case "expr_xor":
                case "expr_or":
                case "expr_logicand":
                case "expr_logicor":
                    {
                        ParseNode_Expr(context, innode.children[0], out var math_left);
                        ParseNode_Expr(context, innode.children[2], out var math_right);

                        var math = new TTCode();
                        math.type = CodeType.EXPR_MATH;
                        math.isExpr = true;
                        math.elements = new CodeElement[4];
                        math.elements[0] = CodeElement.TTType(math_left.GetExprType());
                        math.elements[1] = CodeElement.Other_String(innode.children[1].text);
                        math.elements[2] = CodeElement.TTCode(math_left);
                        math.elements[3] = CodeElement.TTCode(math_right);
                        math.isConst = math_left.isConst && math_right.isConst;
                        outnode = math;
                    }
                    return;
                case "expr_group":
                    {
                        ParseNode_Expr(context, innode.children[1], out outnode);
                    }
                    return;

                case "expr_cond":
                    {
                        var math = new TTCode();
                        math.type = CodeType.EXPR_MATH;
                        math.isExpr = true;

                        ParseNode_Expr(context, innode.children[0], out var math_condition);
                        ParseNode_Expr(context, innode.children[2], out var math_vTrue);
                        ParseNode_Expr(context, innode.children[4], out var math_vFalse);
                        math.elements = new CodeElement[4];
                        math.elements[0] = CodeElement.TTType(math_vTrue.GetExprType());
                        math.elements[1] = CodeElement.TTCode(math_condition);
                        math.elements[2] = CodeElement.TTCode(math_vTrue);
                        math.elements[3] = CodeElement.TTCode(math_vFalse);
                        math.isConst = math_condition.isConst && math_vTrue.isConst && math_vFalse.isConst;
                        outnode = math;
                    }
                    return;
                case "expr_unary":
                    {
                        if (innode.children[0].text == "-" && isConst(innode.children[1]))
                        {
                            ParseNode_Number(context, innode.children[1], out TTCode _node, true);
                            outnode = _node;
                        }
                        else
                        {
                            ParseNode_Expr(context, innode.children[1], out TTCode expr);
                            var token = innode.children[0].text;
                            var unary = new TTCode();
                            unary.type = CodeType.EXPR_UNARY;
                            unary.isExpr = true;
                            unary.isConst = expr.isConst;
                            unary.elements = new CodeElement[3];
                            unary.elements[0] = CodeElement.TTType(expr.GetExprType());
                            unary.elements[1] = CodeElement.Other_String(token);
                            unary.elements[2] = CodeElement.TTCode(expr);

                            outnode = unary;
                        }
                    }
                    return;
                case "expr_postfix":
                    {//後綴表達式 包括1. ++ -- 這樣的自運算  2.call  3.數組 4.member
                        if (innode.children.Count == 2)
                        {

                        }
                        if (innode.children[1].text == "(")//call
                        {
                            ParseNode_Expr_Call(context, innode, out TTCode _nodecall);
                            outnode = _nodecall;
                            return;
                        }
                        if (innode.children[1].text == ".")//member
                        {
                            compiler_expr.ParseNode_Expr_Member(context, innode, out outnode);
                            return;
                        }
                        if (innode.children[1].text == "[")//array
                        {
                            ParseNode_Expr(context, innode.children[0], out TTCode body);
                            TTCode[] indexs = new TTCode[(innode.children.Count - 2) / 2];
                            for (var i = 2; i < innode.children.Count - 1; i += 2)
                            {
                                ParseNode_Expr(context, innode.children[0], out TTCode exprindex);
                                indexs[(i - 2) / 2] = exprindex;
                            }
                            var array = new TTCode();
                            array.type = CodeType.EXPR_ARRAY;
                            array.isExpr = true;

                            //array.isConst;//比较复杂，再说
                            array.elements = new CodeElement[2];
                            //array.elements[0]//比较复杂，再说
                            array.elements[1] = CodeElement.TTCodeArray(indexs);
                            outnode = array;
                            return;
                        }
                    }
                    throw new NotSupportedException();
                case "expr_new":
                    {
                        ParseNode_Expr_New(context, innode, out TTCode _nodecall);
                        outnode = _nodecall;
                    }
                    return;
                case "expr_cast":
                    {
                        ParseNode_Expr_Cast(context, innode, out TTCode _nodecast);
                        outnode = _nodecast;
                    }
                    return;
                default:
                    throw new Exception("not supported.");
            }


        }
        public static void ParseNode_ID_OnlyExpr(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            var id = innode.text;
            if (context.FindTempVar(id, out TempVarContext v))
            {
                var tempvar = new TTCode();
                tempvar.type = CodeType.EXPR_TEMPVAR;
                tempvar.isExpr = true;
                tempvar.elements = new CodeElement[2];
                tempvar.elements[0] = CodeElement.TTType(v.type);
                tempvar.elements[1] = CodeElement.Other_String(v.id);

                outnode = tempvar;
                //tempvar
                return;
            }
            if (context.FindMethod(id, out type.TTMethod method))
            {
                var tempvar = new TTCode();
                tempvar.type = CodeType.EXPR_METHOD;
                tempvar.isExpr = true;

                tempvar.elements = new CodeElement[2];
                tempvar.elements[0] = CodeElement.TTType(method.ReturnType);
                tempvar.elements[1] = CodeElement.TTMethod(method);

                outnode = tempvar;
                return;
            }
            if (context.FindType(id, out type.TTType type))
            {
                var tempvar = new TTCode();
                tempvar.type = CodeType.EXPR_CONTAINER;
                tempvar.isExpr = true;
                tempvar.elements = new CodeElement[1];
                tempvar.elements[0] = CodeElement.TTType(type);
                outnode = tempvar;
                return;
            }
            if (context.FindArg(id, out type.TTArg _arg))
            {
                var tempvar = new TTCode();
                tempvar.type = CodeType.EXPR_ARGVAR;
                tempvar.isExpr = true;
                tempvar.elements = new CodeElement[1];
                tempvar.elements[0] = CodeElement.TTType(_arg.argtype);
                tempvar.elements[0] = CodeElement.TTArg(_arg);
                outnode = tempvar;
                return;
            }
            throw new NotImplementedException();

        }

        public static void ParseNode_IDChain_OnlyExpr(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            //能作爲表達式的idchain,
            //可能是type,
            //可能是臨時變量
            //可能是成員變量
            //可能是以上的組合，這些全都以來使用上下文來解釋
            if (innode.children.Count == 1 && innode.children[0].type == "ID")
            {
                ParseNode_ID_OnlyExpr(context, innode, out outnode);
                return;


            }

            if (innode.children.Count >= 3) //a.b
            {
                //去掉最后一节，先接着解，然后变成一个组合表达式
                var inleft = innode.Cut(0, innode.children.Count - 2);
                var inright = innode.children[innode.children.Count - 1];

                // 因为有namespace这一类可以套很深的表达式， idchain表达式 可能是 [namespace.]*.[type.]*.[var|method];
                //所以左值应该递归
                ParseNode_IDChain_OnlyExpr(context, inleft, out TTCode outleft);
                //ParseNode_ID_OnlyExpr(context, inleft, out node.Node_Expr outleft);
                if (outleft.type == CodeType.EXPR_TEMPVAR)
                {
                    TTCode member = new TTCode();
                    member.type = CodeType.EXPR_MEMBER;
                    member.isExpr = true;
                    member.elements = new CodeElement[3];
                    member.elements[0] = CodeElement.TTType(outleft.GetExprType());
                    member.elements[1] = CodeElement.TTCode(outleft);

                    //member.membername = inright.text;
                    var tttype = outleft.GetExprType();
                    tttype.FindMember(inright.text, out var _vm);
                    if (_vm == null)
                        throw new Exception("should not null.");

                    member.elements[2] = CodeElement.TTMember(_vm);
                    outnode = member;

                    ////临时变量.b
                    ////有两个可能性，成员变量货成员函数，都是成员访问
                    return;

                }
                if (outleft.type == CodeType.EXPR_CONTAINER) //Type.xxx // static member or type.type typechain
                {
                    var container = outleft.GetExprType();
                    //先看看是不是成员访问
                    if (container.FindMember(inright.text, out var _vm))
                    {
                        TTCode member = new TTCode();
                        member.type = CodeType.EXPR_MEMBER;
                        member.isExpr = true;
                        member.elements = new CodeElement[2];
                        member.elements[0] = CodeElement.TTType(container);
                        member.elements[1] = CodeElement.TTMember(_vm);
                        outnode = member;
                        return;
                    }
                    if (container.FindSubContainer(inright.text, out var _sc))
                    {
                        TTCode newcontainer = new TTCode();
                        newcontainer.type = CodeType.EXPR_CONTAINER;
                        newcontainer.isExpr = true;
                        newcontainer.elements = new CodeElement[1];
                        newcontainer.elements[0] = CodeElement.TTType(container);

                        outnode = newcontainer;
                        return;
                    }
                }
                throw new NotImplementedException();

            }
            throw new NotImplementedException();

        }
        public static void ParseNode_Expr_Call(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            //Call节点也是很复杂的，左表达式可以很复杂，如果左表达式很简单，则说明是单纯的函数调用
            var callpart = innode;
            bool ismember = false;
            if (callpart.type == "expr")
            {
                if (callpart.children.Count == 3 && callpart.children[1].text == ".")
                    ismember = true;
                callpart = innode.children[innode.children.Count - 1];
            }
            if (callpart.type == "expr")
                callpart = callpart.children[0];

            outnode = new TTCode();
            outnode.type = CodeType.EXPR_CALL;
            outnode.isExpr = true;

            TTCode outnode_function;
            if (!ismember)
            {
                //简单调用的情况

                ParseNode_Expr(context, callpart.children[0], out outnode_function);//函数用0表达式解析
            }
            else
            {
                ParseNode_Expr_Member(context, innode, out outnode_function);//函数用0表达式解析
            }

            //1= (
            var outnode_args = new List<TTCode>();
            var args = callpart.children[2];
            if (args.text != ")")
            {
                for (var i = 0; i < args.children.Count; i += 2)
                {
                    //i=arg
                    //i+1=","|')"
                    ParseNode_Expr(context, args.children[i], out var _arg);
                    outnode_args.Add(_arg);
                }
            }

            outnode.elements = new CodeElement[3];
            outnode.elements[0] = CodeElement.TTType(outnode_function.GetExprType());
            outnode.elements[1] = CodeElement.TTCode(outnode_function);
            outnode.elements[2] = CodeElement.TTCodeArray(outnode_args);
            return;

        }
        public static void ParseNode_Expr_New(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            //Call节点也是很复杂的，左表达式可以很复杂，如果左表达式很简单，则说明是单纯的函数调用

            outnode = new TTCode();
            outnode.type = CodeType.EXPR_NEW;
            outnode.isExpr = true;

            TTType rettype = null;
            ParseNode_Expr(context, innode.children[1], out var expr);//函数用1表达式解析
            if (expr.type == CodeType.EXPR_CONTAINER)
            {
                rettype = expr.GetExprType();
            }
            else
            {
                throw new Exception("not a ctor call");
            }
            //1= (
            var outnode_args = new List<TTCode>();
            var args = innode.children[3];
            if (args.text != ")")//==4有个0参的情况
            {
                for (var i = 0; i < args.children.Count; i += 2)
                {
                    //i=arg
                    //i+1=","|')"
                    ParseNode_Expr(context, args.children[i], out var _arg);
                    outnode_args.Add(_arg);
                }
            }
            outnode.elements = new CodeElement[2];
            outnode.elements[0] = CodeElement.TTType(rettype);
            outnode.elements[1] = CodeElement.TTCodeArray(outnode_args);
            return;

        }
        public static void ParseNode_Expr_Cast(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            outnode = new TTCode();
            outnode.isExpr = true;//所有的 expr 类型表达式，第一个element 都是返回类型
            outnode.type = CodeType.EXPR_CAST;
            outnode.elements = new CodeElement[2];
            ParseNode_Expr(context, innode.children[1], out var typeexpr);//函数用1表达式解析
            ParseNode_Expr(context, innode.children[3], out var srcexpr);

            if (typeexpr.type == CodeType.EXPR_CONTAINER)
            {
                outnode.isExpr = true;
                outnode.elements[0] = CodeElement.TTType(typeexpr.elements[0].tttype);
            }
            else
            {
                throw new Exception("not a ctor call");
            }
            outnode.elements[1] = CodeElement.TTCode(srcexpr);
            return;
        }
    }
}
